package com.ruoyi.fjtseac.util.aop;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author zzl
 * @date 2022/8/1 21:45
 */
@Aspect
@Component
public class RedisCacheAspect {

    @Autowired
    RedisCache redis;

    @Pointcut("@annotation(com.ruoyi.fjtseac.util.aop.RedisCacheEnable)")
    public void query() {
    }

    @Pointcut("@annotation(com.ruoyi.fjtseac.util.aop.RedisCacheClear)")
    public void update() {
    }

    @Around("query()")
    public Object redisCache(final ProceedingJoinPoint jp) throws Throwable {

        Method method = getMethod(jp);
        StringBuilder buffer = new StringBuilder();
        RedisCacheEnable cache = method.getAnnotation(RedisCacheEnable.class);
        buffer.append(jp.getSignature().getDeclaringTypeName()).append(":").append(jp.getSignature().getName());
        Object[] args = jp.getArgs();
        if (args != null && Objects.requireNonNull(args).length != 0) {
            buffer.append(":").append(Arrays.toString(args));
        }
        //redis的key不能含有空格
        String key = buffer.toString().replaceAll(" ", "");
        Object o = redis.getCacheObject(key);
        if (o == null) {
            Object result = jp.proceed(args);
            final int expire = cache.expire();
            //无论是否查询结果为空均写入redis，避免缓存穿透。
            redis.setCacheObject(key, JSON.toJSONString(result), expire, TimeUnit.SECONDS);
            return result;
        }else {
            AjaxResult cacheData = JSON.parseObject(o.toString(), AjaxResult.class);
            return cacheData;
        }
    }

    private Method getMethod(ProceedingJoinPoint jp) {

        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method runningMethod = signature.getMethod();
        Class<?>[] parameterTypes = runningMethod.getParameterTypes();
        Method method = null;
        try {
            method = jp.getTarget().getClass().getMethod(jp.getSignature().getName(), parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return method;
    }


    @Around("update()")
    public Object cacheClear(final ProceedingJoinPoint jp) throws Throwable {

        String key = jp.getSignature().getDeclaringTypeName() + ":" + "*";
        Collection<String> keys = redis.keys(key);
        System.out.println("keys ==============>" + keys);
        if (keys != null && keys.size() != 0) {
            keys.forEach(k->{
                redis.deleteObject(k);
            });
        }
        return jp.proceed(jp.getArgs());
    }

}
